﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static MyGIS.GISShapefile;
using System.Data.OleDb;
using System.Data;

namespace MyGIS
{
    /// <summary>
    /// 用于对Shapefile进行操作的函数组合类，通常不需要实例化
    /// </summary>
    public class GISShapefile
    {
        [StructLayout(LayoutKind.Sequential, Pack = 4)]//严格按照定义的字节顺序和字节数存储数据，layout：布局
        struct ShapefileHeader//文件头结构
        {
            public int Unused1, Unused2, Unused3, Unused4;
            public int Unused5, Unused6, Unused7, Unused8;
            public int ShapeType;
            public double Xmin, Ymin, Xmax, Ymax;
            public double Unused9, Unused10, Unused11, Unused12;
        }

        static ShapefileHeader ReadFileHeader(BinaryReader br)//读取文件头
        {
            return (ShapefileHeader)GISTools.FromBytes(br, typeof(ShapefileHeader));
        }

        public static GISLayer ReadShapefile(string shpfilename)//读取Shapefile文件
        {
            FileStream fsr = new FileStream(shpfilename, FileMode.Open);
            BinaryReader br = new BinaryReader(fsr);
            ShapefileHeader sfh = ReadFileHeader(br);
            SHAPETYPE ShapeType = (SHAPETYPE)Enum.Parse(
                typeof(SHAPETYPE), sfh.ShapeType.ToString());//读取ShapeType
            GISExtent extent = new GISExtent(new GISVertex(sfh.Xmin, sfh.Ymin), new GISVertex(sfh.Xmax, sfh.Ymax));//读取地图范围
            string dbffilename = shpfilename.Replace(".shp", ".dbf");//更改文件扩展名 .shp-> .dbf
            DataTable table = ReadDBF(dbffilename);
            GISLayer layer = new GISLayer(shpfilename, ShapeType, extent, ReadFields(table));//创建图层
            int rowIndex = 0;//用来记录当前读取的记录位置
            while (br.PeekChar() != -1)//当未读到文件末端时执行
            {
                RecordHeader rh = ReadRecordHeader(br);
                int RecordLength = FromBigToLittle(rh.RecordLength) * 2 - 4;
                byte[] RecordContent = br.ReadBytes(RecordLength);
                if (ShapeType == SHAPETYPE.point)//若判断为点实体
                {
                    GISPoint onepoint = ReadPoint(RecordContent);
                    GISFeature onefeature = new GISFeature(onepoint, ReadAttribute(table, rowIndex));
                    layer.AddFeature(onefeature);
                }
                if (ShapeType == SHAPETYPE.line)//线实体
                {
                    List<GISLine> lines = ReadLines(RecordContent);
                    for (int i = 0; i < lines.Count; i++)
                    {
                        GISFeature onefeature = new GISFeature(lines[i], ReadAttribute(table, rowIndex));
                        layer.AddFeature(onefeature);
                    }
                }
                if (ShapeType == SHAPETYPE.poloygon)//面实体
                {
                    List<GISPolygon> polygons = ReadPolygons(RecordContent);
                    for (int i = 0; i < polygons.Count; i++)
                    {
                        GISFeature onefeature = new GISFeature(polygons[i], ReadAttribute(table, rowIndex));
                        layer.AddFeature(onefeature);
                    }
                }
                rowIndex++;
            }
            br.Close();
            fsr.Close();
            return layer;
        }

        static GISPoint ReadPoint(byte[] RecordContent)
        {
            double x = BitConverter.ToDouble(RecordContent, 0);
            double y = BitConverter.ToDouble(RecordContent, 8);
            return new GISPoint(new GISVertex(x, y));
        }


        /// <summary>
        /// 具有相同类型空间实体的集合（图层）
        /// </summary>
        public enum SHAPETYPE//记录shapefile中存储的空间对象类型
        {
            point = 1,
            line = 3,
            poloygon = 5
        }


        [StructLayout(LayoutKind.Sequential, Pack = 4)]//记录头的结构体
        struct RecordHeader
        {
            public int RecordNumber;
            public int RecordLength;
            public int ShapeType;
        }
        static RecordHeader ReadRecordHeader(BinaryReader br)//读记录头
        {
            return (RecordHeader)GISTools.FromBytes(br, typeof(RecordHeader));
        }

        static int FromBigToLittle(int bigvalue)
        {
            byte[] bigbytes = new byte[4];
            GCHandle handle = GCHandle.Alloc(bigbytes, GCHandleType.Pinned);
            Marshal.StructureToPtr(bigvalue, handle.AddrOfPinnedObject(), false);
            handle.Free();
            byte b2 = bigbytes[2];
            byte b3 = bigbytes[3];
            bigbytes[3] = bigbytes[0];
            bigbytes[2] = bigbytes[1];
            bigbytes[1] = b2;
            bigbytes[0] = b3;
            return BitConverter.ToInt32(bigbytes, 0);
        }

        /// <summary>
        /// 读取线实体
        /// </summary>
        /// <param name="RecordContent"></param>
        /// <returns></returns>
        static List<GISLine> ReadLines(byte[] RecordContent)
        {
            int N = BitConverter.ToInt32(RecordContent, 32);//包含的线实体数量
            int M = BitConverter.ToInt32(RecordContent, 36);//包含的所有线实体的节点数量
            int[] parts = new int[N + 1];

            for (int i = 0; i < N; i++)
            {
                parts[i] = BitConverter.ToInt32(RecordContent, 40 + i * 4);//顺序读取独立的线实体的起始节点坐标
            }
            parts[N] = M;
            List<GISLine> lines = new List<GISLine>();
            for (int i = 0; i < N; i++)//顺序读取该Shapefile中包含的各个线实体
            {
                List<GISVertex> vertexs = new List<GISVertex>();
                for (int j = parts[i]; j < parts[i + 1]; j++)//顺序读取各个线实体的节点
                {
                    double x = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16);
                    double y = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16 + 8);
                    vertexs.Add(new GISVertex(x, y));
                }
                lines.Add(new GISLine(vertexs));
            }
            return lines;
        }


        /// <summary>
        /// 读取面实体
        /// </summary>
        /// <param name="RecordContent"></param>
        /// <returns></returns>
        static List<GISPolygon> ReadPolygons(byte[] RecordContent)
        {
            int N = BitConverter.ToInt32(RecordContent, 32);//包含的面实体数量
            int M = BitConverter.ToInt32(RecordContent, 36);//包含的所有线实体的节点数量
            int[] parts = new int[N + 1];
            for (int i = 0; i < N; i++)
            {
                parts[i] = BitConverter.ToInt32(RecordContent, 40 + i * 4);//顺序读取独立的面实体的起始节点坐标
            }
            parts[N] = M;
            List<GISPolygon> polygons = new List<GISPolygon>();
            for (int i = 0; i < N; i++)//顺序读取该Shapefile中包含的各个线实体
            {
                List<GISVertex> vertexs = new List<GISVertex>();
                for (int j = parts[i]; j < parts[i + 1]; j++)//顺序读取各个线实体的节点
                {
                    double x = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16);
                    double y = BitConverter.ToDouble(RecordContent, 40 + N * 4 + j * 16 + 8);
                    vertexs.Add(new GISVertex(x, y));
                }
                polygons.Add(new GISPolygon(vertexs));
            }
            return polygons;
        }

        /// <summary>
        /// 读取dbf文件
        /// </summary>
        /// <param name="dbffilename"></param>
        /// <returns></returns>
        static DataTable ReadDBF(string dbffilename)
        {
            if (!File.Exists(dbffilename))
            {
                throw new FileNotFoundException("DBF file not found", dbffilename);
            }
                System.IO.FileInfo f = new FileInfo(dbffilename);
                DataSet ds = null;
                string constr = "Provider=Microsoft.Jet.Oledb.4.0;Data Source=" + f.DirectoryName + ";Extended Properties= 'DBASE III'";
                using (OleDbConnection con = new OleDbConnection(constr))
                {
                    var sql = " select * from " + f.Name;
                    OleDbCommand cmd = new OleDbCommand(sql, con);
                    con.Open();
                    ds = new DataSet();
                    OleDbDataAdapter da = new OleDbDataAdapter(cmd);
                    da.Fill(ds);
                }
                return ds.Tables[0];
        }

        /// <summary>
        /// 逐个获取每列的数据类型和字段名称
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        static List<GISField> ReadFields(DataTable table)
        {
            List<GISField> fields = new List<GISField>();
            foreach (DataColumn column in table.Columns)
            {
                fields.Add(new GISField(column.DataType, column.ColumnName));
            }
            return fields;
        }

        /// <summary>
        /// 读取属性：从表中读取给定序号的一行数据，返回个数为Count的一些属性值
        /// </summary>
        /// <param name="table"></param>给定的表
        /// <param name="RowIndex"></param>给定的序号
        /// <returns></returns>
        static GISAttribute ReadAttribute(DataTable table, int RowIndex)
        {
            GISAttribute attribute = new GISAttribute();
            DataRow row = table.Rows[RowIndex];
            for (int i = 0; i < table.Columns.Count; i++)
            {
                attribute.AddValue(row[i]);
            }
            return attribute;
        }
    }


}
